/**
 * 
 */
package ar.edu.utn.frsf.cidisi.ginet.generation.choreography2ginet;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.pnml.tools.epnk.pnmlcoremodel.AnnotationGraphics;
import org.pnml.tools.epnk.pnmlcoremodel.Coordinate;
import org.pnml.tools.epnk.pnmlcoremodel.Graphics;
import org.pnml.tools.epnk.pnmlcoremodel.Name;
import org.pnml.tools.epnk.pnmlcoremodel.Node;
import org.pnml.tools.epnk.pnmlcoremodel.NodeGraphics;
import org.pnml.tools.epnk.pnmlcoremodel.Page;
import org.pnml.tools.epnk.pnmlcoremodel.PetriNet;
import org.pnml.tools.epnk.pnmlcoremodel.PetriNetDoc;
import org.pnml.tools.epnk.pnmlcoremodel.PetriNetType;
import org.pnml.tools.epnk.pnmlcoremodel.PlaceNode;
import org.pnml.tools.epnk.pnmlcoremodel.PnmlcoremodelFactory;
import org.pnml.tools.epnk.pnmlcoremodel.impl.NodeGraphicsImpl;
import org.pnml.tools.epnk.pnmlcoremodel.impl.RefPlaceImpl;
import org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.Declaration;
import org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.HLAnnotation;
import org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.HlpngdefinitionFactory;
import org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.Type;
import org.pnml.tools.epnk.pntypes.hlpngs.datatypes.booleans.BooleansFactory;
import org.pnml.tools.epnk.pntypes.hlpngs.datatypes.integers.IntegersFactory;
import org.pnml.tools.epnk.pntypes.hlpngs.datatypes.terms.Declarations;
import org.pnml.tools.epnk.pntypes.hlpngs.datatypes.terms.MultiSetSort;
import org.pnml.tools.epnk.pntypes.hlpngs.datatypes.terms.NamedSort;
import org.pnml.tools.epnk.pntypes.hlpngs.datatypes.terms.TermsFactory;
import org.pnml.tools.epnk.pntypes.hlpngs.datatypes.terms.UserSort;
import org.pnml.tools.epnk.pntypes.hlpngs.datatypes.terms.Variable;
import org.pnml.tools.epnk.pntypes.hlpngs.datatypes.terms.VariableDecl;

import ar.edu.utn.frsf.cidisi.ginet.Arc;
import ar.edu.utn.frsf.cidisi.ginet.GINet;
import ar.edu.utn.frsf.cidisi.ginet.GinetFactory;
import ar.edu.utn.frsf.cidisi.ginet.Place;
import ar.edu.utn.frsf.cidisi.ginet.Transition;
import ar.edu.utn.frsf.cidisi.ginet.abstractginet.AbstractginetFactory;
import ar.edu.utn.frsf.cidisi.ginet.abstractginet.RefPlace;
import ar.edu.utn.frsf.cidisi.ginet.impl.PlaceImpl;
import ar.edu.utn.frsf.cidisi.ginet.generation.elementsestructure.TreeImpl;
import ar.edu.utn.frsf.cidisi.ginet.generation.elementsestructure.TreeInterface;
import ar.edu.utn.frsf.cidisi.ginet.generation.elementsestructure.UpColbpipStructureToAbstractPage;
import ar.edu.utn.frsf.cidisi.ginet.generation.elementsestructure.elementstemplate.ChoreographyStructure;
import ar.edu.utn.frsf.cidisi.ginet.generation.managers.EMFModelLoad;
import ar.edu.utn.frsf.cidisi.upcolbpip.interactionprotocols.*;
import ar.edu.utn.frsf.cidisi.upcolbpip.interactionprotocols.Exception;

/**
 * @author Neri
 * 
 */
public class Upcolbpip2GINet {

	private InteractionProtocol rootElement;

	private GINet giNet;

	private PetriNetType pnType;

	private PetriNetDoc petriNetDoc;
	
	private Place termination;

	private Map<String, Place> concretePlacesMap = new HashMap<String, Place>();

	private Map<String, Transition> concreteTransitionsMap = new HashMap<String, Transition>();

	private Map<String, org.pnml.tools.epnk.pnmlcoremodel.RefPlace> concreteRefPlacesMap = new HashMap<String, org.pnml.tools.epnk.pnmlcoremodel.RefPlace>();

	public Upcolbpip2GINet(InteractionProtocol rootElement) {
		this.rootElement = rootElement;
		this.petriNetDoc = PnmlcoremodelFactory.eINSTANCE.createPetriNetDoc();
		this.giNet = this.createConcreteGINet();
		this.petriNetDoc.getNet().add(giNet);
	}

	public void saveGINet(String path) {
		this.createPNMLFile(path);
	}

	private GINet createConcreteGINet() {
		
		GINet cginet = GinetFactory.eINSTANCE.createGINet();

		cginet.setId("n1");

		pnType = (PetriNetType) GinetFactory.eINSTANCE.createGINetType();

		cginet.setType(pnType);

		Name nameLabel = PnmlcoremodelFactory.eINSTANCE.createName();

		nameLabel.setText("Concrete GI-Net");

		cginet.setName(nameLabel);
		
		EList<Declaration> declarations = createDeclarations();
		
		cginet.getDeclaration().addAll(declarations);

		return cginet;
	}

	/**
	 * @return
	 *
	 * @author Neri
	 */
	private EList<Declaration> createDeclarations() {
		EList<Declaration> newDecls = new BasicEList<Declaration>();
		
		Declarations newInnerDecls = TermsFactory.eINSTANCE.createDeclarations();
		
		Declaration newDecl = HlpngdefinitionFactory.eINSTANCE.createDeclaration();
		
		NamedSort declName1 = TermsFactory.eINSTANCE.createNamedSort();
		
		org.pnml.tools.epnk.pntypes.hlpngs.datatypes.integers.Integer value = IntegersFactory.eINSTANCE.createInteger();
		
		declName1.setId("GINT");
		declName1.setName("GINT");
		declName1.setDef(value);
		
		newInnerDecls.getDeclaration().add(declName1);
		
		NamedSort declName2 = TermsFactory.eINSTANCE.createNamedSort();
		
		org.pnml.tools.epnk.pntypes.hlpngs.datatypes.integers.Integer value2 = IntegersFactory.eINSTANCE.createInteger();
		
		declName2.setId("CTRL");
		declName2.setName("CTRL");
		declName2.setDef(value2);
		
		newInnerDecls.getDeclaration().add(declName2);
		
		NamedSort declName3 = TermsFactory.eINSTANCE.createNamedSort();
		
		org.pnml.tools.epnk.pntypes.hlpngs.datatypes.booleans.Bool value1 = BooleansFactory.eINSTANCE.createBool();
		
		declName3.setId("COND");
		declName3.setName("COND");
		declName3.setDef(value1);
		
		newInnerDecls.getDeclaration().add(declName3);
		
		VariableDecl declVal3 = TermsFactory.eINSTANCE.createVariableDecl();
		
		UserSort userSort3 = TermsFactory.eINSTANCE.createUserSort();
		
		MultiSetSort multiSort3 = TermsFactory.eINSTANCE.createMultiSetSort();
		
		userSort3.setDeclaration(declName3);
		userSort3.setName("COND");
		
		multiSort3.setBasis(userSort3);
		
		declVal3.setId("cond");
		declVal3.setName("cond");
		declVal3.setSort(multiSort3);
		
		newInnerDecls.getDeclaration().add(declVal3);
		
		VariableDecl declVal = TermsFactory.eINSTANCE.createVariableDecl();
		
		UserSort userSort = TermsFactory.eINSTANCE.createUserSort();
		
		MultiSetSort multiSort = TermsFactory.eINSTANCE.createMultiSetSort();
		
		userSort.setDeclaration(declName1);
		userSort.setName("GINT");
		
		multiSort.setBasis(userSort);
		
		declVal.setId("gi");
		declVal.setName("gi");
		declVal.setSort(multiSort);
		
		newInnerDecls.getDeclaration().add(declVal);
		
		VariableDecl declVal1 = TermsFactory.eINSTANCE.createVariableDecl();
		
		UserSort userSort1 = TermsFactory.eINSTANCE.createUserSort();
		
		MultiSetSort multiSort1 = TermsFactory.eINSTANCE.createMultiSetSort();
		
		userSort1.setDeclaration(declName2);
		userSort1.setName("CTRL");
		
		multiSort1.setBasis(userSort1);
		
		declVal1.setId("ctrl");
		declVal1.setName("ctrl");
		declVal1.setSort(multiSort1);
		
		newInnerDecls.getDeclaration().add(declVal1);
		
		newDecl.setStructure(newInnerDecls);
		
		newDecls.add(newDecl);
		
		return newDecls;	
	}

	public void createConcreteGIModule() {
	
		UpColbpipStructureToAbstractPage upColbPip = new UpColbpipStructureToAbstractPage(this.giNet, this.rootElement);
		
		createConcreteGIModule(this.giNet, upColbPip.getAbstractPage(), 0, 0);
		
		this.setRefToRefPlaces(this.giNet.getPage().get(0), this.termination);
	}

	/**
	 * @param page
	 * @param abstractPage
	 *
	 * @author Neri
	 */
	private void setRefToRefPlaces(Page page, Place termination) {

		EList<Page> pages = this.getPages(page);
		
		for (Page p : pages) {
			EList<RefPlaceImpl> refPlaces = this.getRefPlaces(p);
			for (RefPlaceImpl refPlaceImpl : refPlaces) {
				for (Object object : page.getObject()) {
					if(object instanceof PlaceNode){
						if(((PlaceNode)object).getName().getText().equals(refPlaceImpl.getRef().getName().getText())){
							refPlaceImpl.setRef((PlaceNode)object);
						}else if(refPlaceImpl.getRef().getName().getText().equals(termination.getName().getText())){
							refPlaceImpl.setRef(termination);
						}
					}
				}
			}
		}
		for (Page p : pages) {
			this.setRefToRefPlaces(p, termination);
		}		
	}

	/**
	 * @param page
	 * @return
	 *
	 * @author Neri
	 */
	private EList<Page> getPages(Page page) {
		EList<Page> pages = new BasicEList<Page>();
		
		for (Object object : page.getObject()) {
			if( object instanceof Page){
				pages.add((Page)object);
			}
		}
		
		return pages;
	}
	
	private EList<RefPlaceImpl> getRefPlaces(Page page) {
		EList<RefPlaceImpl> refPlace = new BasicEList<RefPlaceImpl>();
		
		for (Object object : page.getObject()) {
			if( object instanceof RefPlaceImpl){
				refPlace.add((RefPlaceImpl)object);
			}
		}
		
		return refPlace;
	}

	public Page createConcreteGIModule(EObject parent,
			ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page agim, float x,
			float y) {

		String newPageId = agim.getName().getText();
		// Creates the page that represents the concrete GI module
		Page newCgim = createPage(agim, newPageId.toLowerCase(), agim.getName()
				.getText(), x, y);

		if (parent != null && parent instanceof PetriNet) {

			((PetriNet) parent).getPage().add(newCgim);

		} else if (parent instanceof org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.Page) {

			newCgim.setId((((org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.Page) parent)
					.getId() + newPageId).toLowerCase());

			((org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.Page) parent)
					.getObject().add(newCgim);
		}

		EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place> places = ((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page) agim)
				.getPlaces();

		// Create places for the concrete GI module
		EList<Place> newPlaces = createPlaces(newCgim, places);
		newCgim.getObject().addAll(newPlaces);
		// places.put(source.getName().getText(), source);

		// Get transitions from the abstract GI module
		EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition> transitions = ((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page) agim)
				.getTransitions();

		// Create transitions for the concrete GI module
		EList<Transition> newTransitions = createTransitions(newCgim,
				transitions);
		newCgim.getObject().addAll(newTransitions);

		EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.RefPlace> refPlaces = ((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page) agim)
				.getRefPlaces();
		EList<org.pnml.tools.epnk.pnmlcoremodel.RefPlace> newRefPlace = createRefPlaces(
				newCgim, refPlaces);
		newCgim.getObject().addAll(newRefPlace);

		// Get subpages from the abstract GI module
		EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page> subPages = ((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page) agim)
				.getPages();

		// Create subpages for the concrete GI module
		EList<Page> newSubPages = createSubPages(newCgim, subPages);
		newCgim.getObject().addAll(newSubPages);

		// Get arcs from the abstract GI module
		EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Arc> arcs = ((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page) agim)
				.getArcs();

		// Create arcs for the concrete GI module
		EList<Arc> newArcs = createArcs(newCgim, arcs);
		newCgim.getObject().addAll(newArcs);

		return newCgim;
	}

	private Page createPage(Page agim, String id, String name, float x, float y) {

		Page page = (Page) pnType.createPage();
		page.setId(id); // Set the page's id

		NodeGraphics nodeGraphics = PnmlcoremodelFactory.eINSTANCE
				.createNodeGraphics();

		// Creates a coordinate position for the page
		Coordinate position = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		position.setX(x);
		position.setY(y);
		nodeGraphics.setPosition(position);

		// Set the size of the page
		Coordinate size = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		size.setX(40);
		size.setY(40);
		nodeGraphics.setDimension(size);

		page.setGraphics(nodeGraphics);

		// Creates a label for the page
		Name nameLabel = PnmlcoremodelFactory.eINSTANCE.createName();
		nameLabel.setText(name);
		AnnotationGraphics labelPos = PnmlcoremodelFactory.eINSTANCE
				.createAnnotationGraphics();
		Coordinate offset = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		offset.setX(0);
		offset.setY(30);
		labelPos.setOffset(offset);
		nameLabel.setGraphics(labelPos);
		page.setName(nameLabel);

		return page;
	}

	public Resource createPNMLFile(String path) {

		// create the resource and add the net to it
		ResourceSet resourceSet = new ResourceSetImpl();

		final URI uri = URI.createURI("platform:" + path);

		final Resource newResource = resourceSet.createResource(uri);

		EList<EObject> contents = newResource.getContents();

		contents.add(petriNetDoc);

		try {

			newResource.save(new HashMap<Object, Object>());

		} catch (IOException e) {

			return null;

		}

		return newResource;

	}

	private EList<Place> createPlaces(Page cgim,
			EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place> places) {
		EList<Place> np = new BasicEList<Place>();

		for (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place place : places) {

			int cardinality = Integer.parseInt(place.getParallelCardinality());

			if (cardinality > 0) { // Check the cardinality

				// Creates a number of places determined by the cardinality of
				// the current abstract place
				for (int i = 0; i < cardinality; i++) {

					Coordinate position = ((NodeGraphicsImpl) place
							.getGraphics()).getPosition();

					String id = place.getName().getText();

					float yPos = position.getY();

					id += i; // Updates the id
					yPos += 90 * i; // Updates the y coordinate

					// Creates a new place
					Place newPlace = createPlace(pnType, cgim.getId() + id,
							place.getName().getText(), 0, position.getX(),
							yPos, place.getType());

					// Add the newly created place to a map in order to have
					// direct access to such place
					concretePlacesMap.put(newPlace.getId(), newPlace);

					np.add(newPlace);

				}

			} 
		}

		return np;
	}

	private Place createPlace(PetriNetType pnType, String id, String name,
			int marking, float x, float y, Type placeType) {

		Place newPlace = (Place) pnType.createPlace();

		newPlace.setId(id); // Set the id

		NodeGraphics nodeGraphics = PnmlcoremodelFactory.eINSTANCE
				.createNodeGraphics();

		// Creates a coordinate position for the place
		Coordinate position = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		position.setX(x);
		position.setY(y);
		nodeGraphics.setPosition(position);

		// Size of the place
		Coordinate size = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		size.setX(40);
		size.setY(40);
		nodeGraphics.setDimension(size);

		newPlace.setGraphics(nodeGraphics);

		// Creates a label for the place
		Name nameLabel = PnmlcoremodelFactory.eINSTANCE.createName();
		nameLabel.setText(name);
		AnnotationGraphics labelPos = PnmlcoremodelFactory.eINSTANCE
				.createAnnotationGraphics();
		Coordinate offset = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		offset.setX(-20);
		offset.setY(-40);
		labelPos.setOffset(offset);
		nameLabel.setGraphics(labelPos);

		newPlace.setName(nameLabel);

		// Creates the type of the place
		Type newPlaceType = HlpngdefinitionFactory.eINSTANCE.createType();

		if (placeType != null && placeType.getStructure() instanceof UserSort) {

			UserSort userSort = (UserSort) placeType.getStructure();

			UserSort newUserSort = TermsFactory.eINSTANCE.createUserSort();

			if (userSort.getDeclaration() instanceof NamedSort) {

				NamedSort innerDecl = (NamedSort) userSort.getDeclaration();

				NamedSort newDecl = TermsFactory.eINSTANCE.createNamedSort();

				if (innerDecl.getDef() instanceof org.pnml.tools.epnk.pntypes.hlpngs.datatypes.integers.Integer) {

					org.pnml.tools.epnk.pntypes.hlpngs.datatypes.integers.Integer integer = IntegersFactory.eINSTANCE
							.createInteger();

					newDecl.setDef(integer);

				}
				newDecl.setId(innerDecl.getId());

				newDecl.setName(innerDecl.getName());

				newUserSort.setDeclaration(newDecl);

			}

			newPlaceType.setStructure(newUserSort);
			newPlaceType.setText(placeType.getText());

			// Creates a coordinate position for the place type
			AnnotationGraphics newPlaceTypePos = PnmlcoremodelFactory.eINSTANCE
					.createAnnotationGraphics();
			Coordinate newPlaceTypeOffset = PnmlcoremodelFactory.eINSTANCE
					.createCoordinate();
			newPlaceTypeOffset.setX(0);
			newPlaceTypeOffset.setY(30);
			newPlaceTypePos.setOffset(newPlaceTypeOffset);
			newPlaceType.setGraphics(newPlaceTypePos);

			newPlace.setType(newPlaceType);
		}

		// IntegersFactory.eINSTANCE.createInteger();
		//
		// Declaration decl =
		// HlpngdefinitionFactory.eINSTANCE.createDeclaration();
		// decl.g

		// if (marking > 0) {
		// PTMarking markingLabel = PtnetFactory.eINSTANCE.createPTMarking();
		// NonNegativeInteger value = new NonNegativeInteger(""+marking);
		// markingLabel.setText(value);
		// labelPos = PnmlcoremodelFactory.eINSTANCE.createAnnotationGraphics();
		// offset = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		// offset.setX(0);
		// offset.setY(-40);
		// labelPos.setOffset(offset);
		// markingLabel.setGraphics(labelPos);
		// place.setInitialMarking(markingLabel);
		// }
		
		if(newPlace.getName().getText().equals("Termination")){
			this.termination = newPlace;
		}

		return newPlace;
	}

	private EList<Transition> createTransitions(
			Page parentCgim,
			EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition> abstractTransitions) {
		EList<Transition> nt = new BasicEList<Transition>();

		// For each place in the list of abstract places
		for (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition abstractTransition : abstractTransitions) {

			// OJO ACA CON ESTO!!!!
			int sequenceCardinality = 1;
			if (abstractTransition.getSequentialCardinality() != null
					&& abstractTransition.getSequentialCardinality() != "") {

				sequenceCardinality = Integer.valueOf(
						abstractTransition.getSequentialCardinality())
						.intValue();

			}

			// Transitions referencing a sequence page are not associated with
			// any concrete transition
			if (sequenceCardinality == 1) {

				int card = 1;
				if (abstractTransition.getParallelCardinality() != null
						&& !abstractTransition.getParallelCardinality().equals(
								"")) {
					card = Integer.valueOf(
							abstractTransition.getParallelCardinality())
							.intValue();
				}

				if (card > 1) { // Check the cardinality

					// Creates a number of transitions determined by the
					// cardinality of the current abstract place
					for (int i = 0; i < card; i++) {

						Coordinate position = ((NodeGraphicsImpl) abstractTransition
								.getGraphics()).getPosition();

						String id = abstractTransition.getName().getText();

						float yPos = position.getY();

						id += i; // Updates the id
						yPos += 90 * i; // Updates the y coordinate

						// Creates a new transition
						Transition newTransition = createTransition(pnType,
								parentCgim.getId() + id, abstractTransition
										.getName().getText() + i,
								position.getX(), yPos);

						// Add the newly created transition to a map in order to
						// have direct access to such transition
						concreteTransitionsMap.put(newTransition.getId(),
								newTransition);

						nt.add(newTransition);

						// Page newCgim = createConcreteGIModule(i, parentCgim,
						// abstractSubPage, position.getX(), yPos);
						//
						// newTransition.set
						//
						// // Add the newly created subpage to a map in order to
						// have direct access to such subpage
						// concreteSubPagesMap.put(newCgim.getId(), newCgim);
						//
						// parentCgim.getObject().add(newCgim);

					}

				} else {// Creates one transition

					Coordinate position = ((NodeGraphicsImpl) abstractTransition
							.getGraphics()).getPosition();

					Transition newTransition = createTransition(pnType,
							parentCgim.getId()
									+ abstractTransition.getName().getText()
									+ "0", abstractTransition.getName()
									.getText(), position.getX(),
							position.getY());

					concreteTransitionsMap.put(newTransition.getId(),
							newTransition);

					nt.add(newTransition);
				}
			}

		}

		return nt;
	}

	private Transition createTransition(PetriNetType type, String id,
			String name, float x, float y) {

		Transition transition = (Transition) type.createTransition();
		transition.setId(id); // Set the transition's id

		NodeGraphics nodeGraphics = PnmlcoremodelFactory.eINSTANCE
				.createNodeGraphics();

		// Creates a coordinate position for the transition
		Coordinate position = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		position.setX(x);
		position.setY(y);
		nodeGraphics.setPosition(position);

		// Set the size of the transition
		Coordinate size = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		size.setX(40);
		size.setY(40);
		nodeGraphics.setDimension(size);

		transition.setGraphics(nodeGraphics);

		// Creates a label for the transition
		Name nameLabel = PnmlcoremodelFactory.eINSTANCE.createName();
		nameLabel.setText(name);
		AnnotationGraphics labelPos = PnmlcoremodelFactory.eINSTANCE
				.createAnnotationGraphics();
		Coordinate offset = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		offset.setX(0);
		offset.setY(30);
		labelPos.setOffset(offset);
		nameLabel.setGraphics(labelPos);
		transition.setName(nameLabel);

		return transition;
	}

	private EList<org.pnml.tools.epnk.pnmlcoremodel.RefPlace> createRefPlaces(
			Page cgim, EList<RefPlace> abstractRefPlaces) {
		EList<org.pnml.tools.epnk.pnmlcoremodel.RefPlace> nrp = new BasicEList<org.pnml.tools.epnk.pnmlcoremodel.RefPlace>();

		// For each place in the list of abstract places
		for (RefPlace abstractRefPlace : abstractRefPlaces) {

			int card = 1;
			if (abstractRefPlace.getParallelCardinality() != null
					&& !abstractRefPlace.getParallelCardinality().equals("")) {
				card = Integer.valueOf(
						abstractRefPlace.getParallelCardinality()).intValue();
			}

			if (card > 0) { // Check the cardinality

				// Creates a number of places determined by the cardinality of
				// the current abstract place
				for (int i = 0; i < card; i++) {

					Coordinate position = ((NodeGraphicsImpl) abstractRefPlace
							.getGraphics()).getPosition();

					String id = abstractRefPlace.getName().getText();

					float yPos = position.getY();

					id += i; // Updates the id
					yPos += 90 * i; // Updates the y coordinate

					// Creates a new place PetriNetType pnType, String id,
					// String name, int marking, float x, float y, Type
					// placeType
					org.pnml.tools.epnk.pnmlcoremodel.RefPlace newRefPlace = createRefPlace(
							cgim.getId() + id, abstractRefPlace.getName()
									.getText(), position.getX(), yPos, abstractRefPlace.getRef());

					// Add the newly created place to a map in order to have
					// direct access to such place
					concreteRefPlacesMap.put(newRefPlace.getId(), newRefPlace);

					nrp.add(newRefPlace);

				}

			}

		}

		return nrp;
	}

	private org.pnml.tools.epnk.pnmlcoremodel.RefPlace createRefPlace(
			String id, String name, float x, float y, PlaceNode place) {
		org.pnml.tools.epnk.pnmlcoremodel.RefPlace refPlace = PnmlcoremodelFactory.eINSTANCE
				.createRefPlace();
		refPlace.setId(id); // Set the ref places's id

		NodeGraphics nodeGraphics = PnmlcoremodelFactory.eINSTANCE
				.createNodeGraphics();

		// Creates a coordinate position for the ref place
		Coordinate position = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		position.setX(x);
		position.setY(y);
		nodeGraphics.setPosition(position);

		// Set the size of the ref place
		Coordinate size = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		size.setX(40);
		size.setY(40);
		nodeGraphics.setDimension(size);

		refPlace.setGraphics(nodeGraphics);

		// Creates a label for the ref place
		Name nameLabel = PnmlcoremodelFactory.eINSTANCE.createName();
		nameLabel.setText(name);
		AnnotationGraphics labelPos = PnmlcoremodelFactory.eINSTANCE
				.createAnnotationGraphics();
		Coordinate offset = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		offset.setX(0);
		offset.setY(30);
		labelPos.setOffset(offset);
		nameLabel.setGraphics(labelPos);
		refPlace.setName(nameLabel);
		
		if(place != null){
			refPlace.setRef(place);
		}

		return refPlace;
	}

	private EList<Arc> createArcs(Page cgim,
			EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Arc> arcs) {

		EList<Arc> newArcs = new BasicEList<Arc>();

		// Iterates over all arcs of the Abstract GI module
		for (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Arc arc : arcs) {

			// Get source and target of current arc
			Node sourceNode = arc.getSource();
			Node targetNode = arc.getTarget();

			int sourceCardinality = 1;
			int targetCardinality = 1;

			boolean isSequence = false;

			sourceCardinality = getParallelCardinality(sourceNode);

			if (getSequentialCardinality(sourceNode) != 1)
				isSequence = true;

			targetCardinality = getParallelCardinality(targetNode);

			if (getSequentialCardinality(targetNode) != 1)
				isSequence = true;

			if (!isSequence) {

				if (sourceCardinality > 1) { // Many sources

					if (targetCardinality > 1) { // Many sources - Many targets

						// Creates one arc that connects each source to its
						// corresponding target
						for (int i = 0; i < sourceCardinality; i++) {

							String sourceNodeId = cgim.getId()
									+ sourceNode.getName().getText() + i;
							String targetNodeId = cgim.getId()
									+ targetNode.getName().getText() + i;

							Node newSource = getConcreteNode(sourceNodeId,
									sourceNode);

							Node newTarget = getConcreteNode(targetNodeId,
									targetNode);

							Arc newArc = createArc(pnType, sourceNodeId
									+ targetNodeId, newSource, newTarget, arc);

							newArcs.add(newArc);

						}

					} else if (targetCardinality <= 1) { // Many sources - One
															// target

						// Creates one arc that connects each source to one
						// target
						for (int i = 0; i < sourceCardinality; i++) {

							String sourceNodeId = cgim.getId()
									+ sourceNode.getName().getText() + i;
							String targetNodeId = cgim.getId()
									+ targetNode.getName().getText() + "0";

							Node newSource = getConcreteNode(sourceNodeId,
									sourceNode);

							Node newTarget = getConcreteNode(targetNodeId,
									targetNode);

							Arc newArc = createArc(pnType, sourceNodeId
									+ targetNodeId, newSource, newTarget, arc);

							newArcs.add(newArc);
						}

					}

				} else if (sourceCardinality <= 1) { // One source

					if (targetCardinality > 1) { // One source - Many targets

						// Creates one arc that connects each target to one
						// source
						for (int i = 0; i < targetCardinality; i++) {

							String sourceNodeId = cgim.getId()
									+ sourceNode.getName().getText() + "0";
							String targetNodeId = cgim.getId()
									+ targetNode.getName().getText() + i;

							Node newSource = getConcreteNode(sourceNodeId,
									sourceNode);

							Node newTarget = getConcreteNode(targetNodeId,
									targetNode);

							Arc newArc = createArc(pnType, sourceNodeId
									+ targetNodeId, newSource, newTarget, arc);

							newArcs.add(newArc);

						}
					} else if (targetCardinality <= 1) { // One source - One
															// target
						Random rand = new Random();
						int x = rand.nextInt(10);
						// Creates one arc that connects one source and one
						// target

						String sourceNodeId = cgim.getId()
								+ sourceNode.getName().getText() + "0";
						String targetNodeId = cgim.getId()
								+ targetNode.getName().getText() + "0";

						Node newSource = getConcreteNode(sourceNodeId,
								sourceNode);

						Node newTarget = getConcreteNode(targetNodeId,
								targetNode);

						Arc newArc = createArc(pnType, sourceNodeId
								+ targetNodeId + x, newSource, newTarget, arc);

						newArcs.add(newArc);

					}

				}

			}

		}

		return newArcs;

	}

	private Node getConcreteNode(String id, Node node) {

		Node newNode = null;

		if (node instanceof ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place) {
			newNode = concretePlacesMap.get(id);
			// if
			// (((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place)sourceNode).getRef()!=null)
			// newSource = concreteRefPlacesMap.get(idSource);
		} else if (node instanceof RefPlace)

			newNode = concreteRefPlacesMap.get(id);

		else if (node instanceof ar.edu.utn.frsf.cidisi.ginet.Transition)

			newNode = concreteTransitionsMap.get(id);

		return newNode;

	}

	private Arc createArc(PetriNetType type, String id, Node source,
			Node target, Arc arc) {

		Arc newArc = (Arc) type.createArc();

		newArc.setId(id);

		newArc.setSource(source);

		newArc.setTarget(target);

		HLAnnotation annotation = HlpngdefinitionFactory.eINSTANCE
				.createHLAnnotation();
		AnnotationGraphics labelPos = PnmlcoremodelFactory.eINSTANCE
				.createAnnotationGraphics();
		Coordinate offset = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		offset.setX(0);
		offset.setY(30);
		labelPos.setOffset(offset);
		annotation.setGraphics(labelPos);
		
		Graphics graphic = ((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Arc)arc).getGraphics();
		
		newArc.setGraphics(graphic);

		HLAnnotation hlAnnotation = arc.getHlinscription();
		if (hlAnnotation != null) {
			Variable newVar = TermsFactory.eINSTANCE.createVariable();

			if (hlAnnotation.getStructure() instanceof Variable) {
				Variable var = (Variable) hlAnnotation.getStructure();

				VariableDecl varDecl = TermsFactory.eINSTANCE
						.createVariableDecl();
				varDecl.setId(var.getRefvariable().getId());
				varDecl.setName(var.getRefvariable().getName());
				varDecl.setSort(var.getSort().clone());

				newVar.setRefvariable(varDecl);
				newVar.setName(var.getName());

			}
			annotation.setStructure(newVar);
			annotation.setText(hlAnnotation.getText());
		} else {
			annotation.setText("");
		}

		newArc.setHlinscription(annotation);

		// Name nameLabel = PnmlcoremodelFactory.eINSTANCE.createName();
		// nameLabel.setText("");
		// AnnotationGraphics labelPos =
		// PnmlcoremodelFactory.eINSTANCE.createAnnotationGraphics();
		// Coordinate offset =
		// PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		// offset.setX(0);
		// offset.setY(30);
		// labelPos.setOffset(offset);
		// nameLabel.setGraphics(labelPos);
		// arc.setName(nameLabel);

		// Coordinate coordinate =
		// PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		// coordinate.setX(300);
		// coordinate.setY(60);
		// ArcGraphics arcGraphics =
		// PnmlcoremodelFactory.eINSTANCE.createArcGraphics();
		// arcGraphics.getPosition().add(coordinate);
		// arc.setGraphics(arcGraphics);

		return newArc;
	}

	private int getParallelCardinality(Node node) {

		int cardinality = 1;

		// Get the cardinality of the arc's node
		if (node instanceof ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place) {

			// TODO: Check that the cardinality is not null, otherwise null
			// pointer exception is raised.-
			cardinality = Integer.valueOf(
					((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place) node)
							.getParallelCardinality()).intValue();

		} else if (node instanceof RefPlace) { // TODO: It is also necessary to
												// include the RefTransition

			cardinality = Integer.valueOf(
					((RefPlace) node).getParallelCardinality()).intValue();
			// cardinality = 1;

		} else if (node instanceof ar.edu.utn.frsf.cidisi.ginet.Transition) {

			cardinality = Integer
					.valueOf(
							((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition) node)
									.getParallelCardinality()).intValue();

		}

		return cardinality;

	}

	private int getSequentialCardinality(Node node) {

		int cardinality = 1;

		// Get the cardinality of the arc's source
		if (node instanceof ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition) {
			if (((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition) node)
					.getSequentialCardinality() != null
					&& ((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition) node)
							.getSequentialCardinality() != "") {

				cardinality = Integer
						.valueOf(((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition) node)
								.getSequentialCardinality());

			}

		}

		return cardinality;

	}

	private EList<Page> createSubPages(
			Page parentCgim,
			EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page> abstractSubPages) {
		EList<Page> np = new BasicEList<Page>();

		// For each place in the list of abstract subpages
		for (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page abstractSubPage : abstractSubPages) {

			// int card = 1;
			// if (abstractSubPage.getCardinality() != null &&
			// !abstractSubPage.getCardinality().equals("")){
			// card =
			// Integer.valueOf(abstractSubPage.getCardinality()).intValue();
			// }
			//
			// if (card > 1){ // Check the cardinality
			//
			// // Creates a number of subpages determined by the cardinality of
			// the current abstract subpage
			// for (int i=1; i<=card; i++){
			//
			// Coordinate position =
			// ((NodeGraphicsImpl)abstractSubPage.getGraphics()).getPosition();
			//
			// String id = abstractSubPage.getId();
			//
			// float yPos = position.getY();
			//
			// id += i; // Updates the id
			// yPos += 90*(i-1); // Updates the y coordinate
			//
			// Page newCgim = createConcreteGIModule(i, parentCgim,
			// abstractSubPage, position.getX(), yPos);
			//
			// // // Adds the new concrete subpage to the parent page cgim
			// // cgim.getObject().add(newCgim);
			//
			// // // Creates a new subpage
			// // Page newSubPage = createPage(agim, id, id, position.getX(),
			// yPos);
			//
			// // Add the newly created subpage to a map in order to have direct
			// access to such subpage
			// concreteSubPagesMap.put(newCgim.getId(), newCgim);
			//
			// np.add(newCgim);
			//
			// // addConcreteGIModuleTo(subPage, cgim);
			//
			// }
			//
			// } else { // Creates one subpage

			Coordinate position = ((NodeGraphicsImpl) abstractSubPage
					.getGraphics()).getPosition();

			// Page newSubPage = createPage(agim, subPage.getId(),
			// subPage.getId(),
			// position.getX(), position.getY());

			// addConcreteGIModuleTo(subPage, cgim);
			Page newCgim = createConcreteGIModule(parentCgim,
					abstractSubPage, position.getX(), position.getY());

			// // Adds the new concrete subpage to the parent page cgim
			// cgim.getObject().add(newCgim);

			np.add(newCgim);

			// }

		}

		return np;
	}
}
